package TDengineDB

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

//DataHandFn 数据处理方法
type DataHandFn func(key string,sVal interface{})(tVal interface{})

//ExecuteResult 执行失败结果
//{
//	"status":"error",
//	"code":897,
//	"desc":"Database already exists"
//}
//执行成功结果
//{
//	"status":"succ",
//	"head":["ts","current","voltage","phase","location","groupdid"],
//	"data":[["2020-09-09 17:16:47.332",10.20000,219,0.32000,"Beijing.Chaoyang",2]],
//	"rows":1
//}
/*
说明：

status: 告知操作结果是成功还是失败。
head: 表的定义，如果不返回结果集，则仅有一列“affected_rows”。（从 2.0.17 版本开始，建议不要依赖 head 返回值来判断数据列类型，而推荐使用 column_meta。在未来版本中，有可能会从返回值中去掉 head 这一项。）
column_meta: 从 2.0.17 版本开始，返回值中增加这一项来说明 data 里每一列的数据类型。具体每个列会用三个值来说明，分别为：列名、列类型、类型长度。例如["current",6,4]表示列名为“current”；列类型为 6，也即 float 类型；类型长度为 4，也即对应 4 个字节表示的 float。如果列类型为 binary 或 nchar，则类型长度表示该列最多可以保存的内容长度，而不是本次返回值中的具体数据长度。当列类型是 nchar 的时候，其类型长度表示可以保存的 unicode 字符数量，而不是 bytes。
data: 具体返回的数据，一行一行的呈现，如果不返回结果集，那么就仅有[[affected_rows]]。data 中每一行的数据列顺序，与 column_meta 中描述数据列的顺序完全一致。
rows: 表明总共多少行数据。
column_meta 中的列类型说明：
1：BOOL
2：TINYINT
3：SMALLINT
4：INT
5：BIGINT
6：FLOAT
7：DOUBLE
8：BINARY
9：TIMESTAMP
10：NCHAR
*/
type ExecuteResult struct {
	ColNames  []string        //列名
	ColTypes  []int           //列类型
	ColLength []int64         //列长度
	Data      [][]interface{} //数据二维数组切片
	Rows      int             //影响行数
	Code      int             //错误码
	Desc      string          //错误描述
}

//GetAffectedRows 获取受影响行数
func (e *ExecuteResult) GetAffectedRows() (int64,error) {
	if len(e.Data) == 0 {
		return 0,  errors.New("结果集为空")
	}
	val := e.Data[0][0]
	switch val.(type) {
	case int64:
		return val.(int64), nil
	case uint64:
		return int64(val.(uint64)), nil
	case float64:
		return int64(val.(float64)), nil
	case float32:
		return int64(val.(float32)), nil
	case int32:
		return int64(val.(int32)), nil
	case uint32:
		return int64(val.(uint32)), nil
	case int:
		return int64(val.(int)), nil
	}
	return 0, errors.New(fmt.Sprint("查询结果转换异常：", val))
}

//ToSliceMap 获取执行结果转切片map
func (e *ExecuteResult) ToSliceMap() ([]map[string]interface{},error) {
	// 2.0.17 版本
	result := make([]map[string]interface{}, 0, len(e.Data))
	for _, rows := range e.Data {
		count := len(rows)
		row := make(map[string]interface{}, count)
		for i := 0; i < count; i++ {
			row[e.ColNames[i]] = rows[i]
		}
		result = append(result, row)
	}
	return result, nil
}

//ToSlice 转换为切片对象
func (e *ExecuteResult) ToSlice(results interface{}) error {
	reflectRawSlice := reflect.TypeOf(results)
	rawKind := reflectRawSlice.Kind()
	rawElement := reflectRawSlice.Elem()
	if (rawKind == reflect.Ptr && rawElement.Kind() != reflect.Slice) ||
		(rawKind != reflect.Ptr && rawKind != reflect.Slice) {
		return fmt.Errorf("Incompatible Value, Looking For Slice : %v : %v", rawKind, rawElement.Kind())
	}
	// Create a slice large enough to decode all the values
	valSlice := reflect.MakeSlice(rawElement, len(e.Data), len(e.Data))
	rowIndex := make(map[string]int, len(e.ColNames))
	for key,val := range e.ColNames {
		rowIndex[strings.ToLower(val)] = key
	}
	for key, rows := range e.Data {
		//创建新实例
		sliceElementType := rawElement.Elem()
		var obj reflect.Value //对象的指针对应的reflect.Value
		if sliceElementType.Kind() != reflect.Ptr {
			// A slice of objects
			obj = reflect.New(rawElement.Elem()) //返回对象的指针对应的reflect.Value
		} else {
			// A slice of pointers
			obj = reflect.New(rawElement.Elem().Elem()) //返回对象的指针对应的reflect.Value
		}
		trueValue:= obj.Elem()
		objType := trueValue.Type()
		for i:= 0; i < objType.NumField();i++ {
			fieldType := objType.Field(i)
			name := strings.ToLower(fieldType.Name)
			if val,ok := rowIndex[name];ok {
				fieldVal := trueValue.Field(i)
				typ := e.ColTypes[val]
				switch typ {
				case 0: //TSDB_DATA_TYPE_NULL
				break
				case 1: //TSDB_DATA_TYPE_BOOL
					fieldVal.SetBool(rows[val].(bool))
					break
				case 2: //TSDB_DATA_TYPE_TINYINT
					fieldVal.SetInt(int64(rows[val].(int8)))
					break
				case 3: //TSDB_DATA_TYPE_SMALLINT
					fieldVal.SetInt(int64(rows[val].(int16)))
					break
				case 4: //TSDB_DATA_TYPE_INT
					fieldVal.SetInt(int64(rows[val].(int32)))
					break
				case 5: //TSDB_DATA_TYPE_BIGINT
					fieldVal.SetInt(rows[val].(int64))
					break
				case 6://TSDB_DATA_TYPE_FLOAT
					fieldVal.SetFloat(float64(rows[val].(float32)))
					break
				case 7://TSDB_DATA_TYPE_DOUBLE
					fieldVal.SetFloat(rows[val].(float64))
					break
				case 8://TSDB_DATA_TYPE_BINARY
					fieldVal.SetString(rows[val].(string))
					break
				case 9: //TSDB_DATA_TYPE_TIMESTAMP
					fieldVal.Set(reflect.ValueOf(rows[val]))
					break
				case 10://TSDB_DATA_TYPE_NCHAR
					fieldVal.SetString(rows[val].(string))
					break
				case 11://TSDB_DATA_TYPE_UTINYINT
					fieldVal.SetUint(uint64(rows[val].(uint8)))
					break
				case 12://TSDB_DATA_TYPE_USMALLINT
					fieldVal.SetUint(uint64(rows[val].(uint16)))
					break
				case 13://TSDB_DATA_TYPE_UINT
					fieldVal.SetUint(uint64(rows[val].(uint32)))
					break
				case 14://TSDB_DATA_TYPE_UBIGINT
					fieldVal.SetUint(rows[val].(uint64))
					break
				}
			}
		}
		indexVal := valSlice.Index(key)
		indexVal.Set(obj)
	}
	reflect.ValueOf(results).Elem().Set(valSlice)
	return nil
}

